package maosy.oauth2.password.config

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.PropertySource
import org.springframework.data.redis.connection.RedisConnectionFactory
import org.springframework.http.HttpMethod
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.crypto.factory.PasswordEncoderFactories
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer
import org.springframework.security.oauth2.provider.token.ConsumerTokenServices
import org.springframework.security.oauth2.provider.token.DefaultTokenServices
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore

@Configuration
@EnableAuthorizationServer
@PropertySource("classpath:props/oauth.properties")
open class OAuth2ServerConfig : AuthorizationServerConfigurerAdapter() {

    @Autowired
    lateinit var authenticationManager: AuthenticationManager
    @Autowired
    lateinit var redisConnectionFactory: RedisConnectionFactory

    @Value("\${client.appId}")
    lateinit var appId: String
    @Value("\${client.secret}")
    lateinit var appSecret: String

    override fun configure(security: AuthorizationServerSecurityConfigurer?) {
        security?.apply {
            tokenKeyAccess("permitAll()")
            checkTokenAccess("permitAll()")
            allowFormAuthenticationForClients()
        }
//        super.configure(security)
    }

    @Bean
    open fun tokenService(): ResourceServerTokenServices {
        return DefaultTokenServices().apply {
            setTokenStore(RedisTokenStore(redisConnectionFactory))
            setSupportRefreshToken(true)
        }
    }

    @Autowired
    @Qualifier("userDetailsService")
    lateinit var userDetailService: UserDetailsService

    override fun configure(endpoints: AuthorizationServerEndpointsConfigurer?) {
        endpoints?.apply {
            tokenStore(RedisTokenStore(redisConnectionFactory))
            userDetailsService(userDetailService)
            authenticationManager(authenticationManager)
            allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
        }
//        super.configure(endpoints)
    }

    override fun configure(clients: ClientDetailsServiceConfigurer?) {
        val secret = BCryptPasswordEncoder().encode(appSecret)
        clients?.apply {
            inMemory()
                .withClient(appId)
                .secret(secret)
                .authorizedGrantTypes("client_credentials", "password", "refresh_token")
                .scopes("all")
                .resourceIds("oauth2-resource")
                .accessTokenValiditySeconds(20 * 60)
                .refreshTokenValiditySeconds(2 * 60 * 60)
                .authorities("API_CLIENT")
        }
    }
}